package service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import bean.Message;
import bean.Message.Action;

public class ServidorService {
	private ServerSocket serverSocket;
	private Socket socket;
	private Map<String, ObjectOutputStream> mapMatches = new HashMap<String, ObjectOutputStream>();
	private Map<String, ObjectOutputStream> mapOnlines = new HashMap<String, ObjectOutputStream>();
	private Map<String, String> mapPlayers = new HashMap<String, String>();
	private Map<String, Integer> mapPoints = new HashMap<String, Integer>();
	private Map<String, String> mapWords = new HashMap<String, String>();

	public ServidorService() {
		mapWords.put("Circo", "MALABARISTA");
		mapWords.put("Tecnologia", "MICROSOFT");
		mapWords.put("Guerra", "CANUDOS");
		mapWords.put("Lugar", "AZERBAIJAO");
		mapWords.put("Aventura", "TIROLESA");
		mapWords.put("Escola", "GRAMPEADOR");
		mapWords.put("Cozinha", "OREGANO");
		mapWords.put("Navio", "TITANIC");
		mapWords.put("Comida", "ALPISTE");
		mapWords.put("Instrumento", "XILOFONE");
		try {
			serverSocket = new ServerSocket(5556);
			System.out
					.println("                              +++SERVIDO FUNCIONANDO++++");

			while (true) {
				socket = serverSocket.accept();
				new Thread(new ListenerSocket(socket)).start();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private class ListenerSocket implements Runnable {

		private ObjectOutputStream output;
		private ObjectInputStream input;

		public ListenerSocket(Socket socket) {
			try {
				this.output = new ObjectOutputStream(socket.getOutputStream());
				this.input = new ObjectInputStream(socket.getInputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		@Override
		public void run() {

			Message message = null;

			try {
				while ((message = (Message) input.readObject()) != null) {
					Action action = message.getAction();

					if (action.equals(Action.CONNECT)) {
						
						writer(message.getName());
						reader();
						
						boolean isConnected = connect(message, output);
						if (isConnected) {
				//			System.out.println("TAM mapOnlines na conexão: "+mapOnlines.size());
						//	mapOnlines.put(message.getName(), output);

							sendMatchesToOne(message, output);
				//			System.out.println("TAM mapOnlines na conexão: "+mapOnlines.size());
							// sendOnlines();
							sendMatches();
						}
					} else if (action.equals(Action.CREATE_GAME)) {
						boolean isCreated = createMatche(message, output);
						if (isCreated) {
							mapMatches.put(message.getPartida(), output);
							sendMatches();
							sendCreatedGame(message, output);
						}

					} else if (action.equals(Action.DISCONNECT)) {
						disconnect(message, output);
						sendOnlines();
						return;

					} else if (action.equals(Action.SEND_ALL)) {
						sendAll(message);
						System.out.println("SENDALL");

					} else if (action.equals(Action.SEND_ONE)) {
						sendOne(message);
						System.out.println("SENDONE");

					} else if (action.equals(Action.MATCHES)) {
						sendMatchesToOne(message, output);
					} else if (action.equals(Action.REMOVE_MATCH)) {
						remove(message);
					} else if (action.equals(Action.PLAY)) {
						mapPoints.put(message.getName(), 0);
						insertPlayer(message, output);
					} else if (action.equals(Action.NEW_WORD)) {
						sendNewWord(message);
					} else if (action.equals(Action.RODA)) {
						rodaRoda(message);
					}else if (action.equals(Action.NEXT_PLAYER)) {
						nextPlayer(message);
					}else if (action.equals(Action.GUESS)) {
						System.out.println("RECEBI UM PALPITE!!!!");
						sendGuess(message);
					}

				}
			} catch (ClassNotFoundException | IOException e) {
				// TODO Auto-generated catch block
				//disconnect(message, output);
				//System.out.println(message.getName() + "Deixou a sala.");
			}

		}
		
		private void nextPlayer(Message message) {
			System.out.println("N PLAYERS: "+message.getNplayers());

			if(message.getNplayers().equals("4")){
				if(message.getCurrentPlayer().equals("0")){
					message.setNextPlayer("1");
				}else if(message.getCurrentPlayer().equals("1")){
					message.setNextPlayer("2");
				}else if(message.getCurrentPlayer().equals("2")){
					message.setNextPlayer("3");
				}else if(message.getCurrentPlayer().equals("3")){
					message.setNextPlayer("4");
				}else if(message.getCurrentPlayer().equals("4")){
					message.setNextPlayer("1");
				}
			}else if(message.getNplayers().equals("3")){
				if(message.getCurrentPlayer().equals("0")){
					message.setNextPlayer("1");
				}else if(message.getCurrentPlayer().equals("1")){
					message.setNextPlayer("2");
				}else if(message.getCurrentPlayer().equals("2")){
					message.setNextPlayer("3");
				}else if(message.getCurrentPlayer().equals("3")){
					message.setNextPlayer("1");
				}
			}else if(message.getNplayers().equals("2")){
				if(message.getCurrentPlayer().equals("0")){
					message.setNextPlayer("1");
				}else if(message.getCurrentPlayer().equals("1")){
					message.setNextPlayer("2");
				}else if(message.getCurrentPlayer().equals("2")){
					message.setNextPlayer("1");
				}
			}else if(message.getNplayers().equals("1")){
				message.setNextPlayer("1");
			}
			System.out.println("NEXT PLAY: "+message.getNextPlayer());
			for (Map.Entry<String, String> kv : mapPlayers.entrySet()) {
				if (kv.getValue().equals(message.getPartida())) {
					ObjectOutputStream output2 = mapOnlines.get(kv.getKey());
					try {
						output2.writeObject(message);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			
		}

		private void sendGuess(Message message){
			for (Map.Entry<String, String> kv : mapPlayers.entrySet()) {
				if (kv.getValue().equals(message.getPartida())) {
					ObjectOutputStream output2 = mapOnlines.get(kv.getKey());
					try {
						output2.writeObject(message);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}




	}
	
    private void writer(String texto) throws IOException {
        BufferedWriter buffWrite = new BufferedWriter(new FileWriter("src/data/rank.txt"));
        buffWrite.append(texto + "\n");
        buffWrite.close();
    }
    
    private void reader() throws IOException {
        BufferedReader buffRead = new BufferedReader(new FileReader("src/data/rank.txt"));
        String linha = "";
        while (true) {
            if (linha != null) {
                System.out.println(linha);
  
            } else
                break;
            linha = buffRead.readLine();
        }
        buffRead.close();
    }
	
	private void rodaRoda(Message message) {
		Random randomGenerator = new Random();
		int rodada = randomGenerator.nextInt(6);
		message.setRodada(rodada);


		for (Map.Entry<String, String> kv : mapPlayers.entrySet()) {
			if (kv.getValue().equals(message.getPartida())) {
				ObjectOutputStream output2 = mapOnlines.get(kv.getKey());
				try {
					output2.writeObject(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	private void sendNewWord(Message message) {
		Random randomGenerator = new Random();
		int randomInt = randomGenerator.nextInt(mapWords.size());
		String key = (new ArrayList<String>(mapWords.keySet()))
				.get(randomInt);
		String value = (new ArrayList<String>(mapWords.values()))
				.get(randomInt);
		mapWords.remove(key);
		System.out.println("Generated : " + key + " - " + value);

		message.setDica(key);
		message.setPalavra(value);

		for (Map.Entry<String, String> kv : mapPlayers.entrySet()) {
			if (kv.getValue().equals(message.getPartida())) {
				ObjectOutputStream output2 = mapOnlines.get(kv.getKey());
				try {
					output2.writeObject(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}
	
	private void insertPlayer(Message message, ObjectOutputStream output) {
		System.out.println("+++++++!!!");
//		ObjectOutputStream output = mapOnlines.get(message.getName());
	    Map<String, Integer> mapTemporaryPoints = new HashMap<String, Integer>();
		for (Map.Entry<String, String> kv : mapPlayers.entrySet()) {
			System.out.println("1");
			if (kv.getValue().equals(message.getPartida())) {
				mapTemporaryPoints.put(kv.getKey(), mapPoints.get(kv.getKey()));
			}
		}

		if (mapTemporaryPoints.size() < 4) {
			message.setText("SIM");
			mapTemporaryPoints.put(message.getName(), 0);
			message.setMapPoints(mapTemporaryPoints);
			mapPlayers.put(message.getName(), message.getPartida());
			message.setAction(Action.NEW_USER);
			System.out.println("TAM mapPlayers: "+mapPlayers.size());
			System.out.println("TAM mapOnlines: "+mapOnlines.size());
			for (Map.Entry<String, String> kv : mapPlayers.entrySet()) {
				System.out.println("Message Name: "+message.getName());
				System.out.println("Jogador: "+kv.getKey());
				if (kv.getValue().equals(message.getPartida())) {
					ObjectOutputStream output2 = mapOnlines
							.get(kv.getKey());
					System.out.println(output2.toString());
					try {
						System.out.println("VAI");
						output2.writeObject(message);
						System.out.println("FOI");
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("********");
			}

			System.out.println("ENTROU NA SALA");
			System.out.println("TAM DO MAP: " + mapPlayers.size());

		} else {
			message.setText("NAO");
			try {
				output.writeObject(message);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}
	private boolean connect(Message message, ObjectOutputStream output) {
		if (mapOnlines.size() == 0) {
			message.setText("SIM!");
			send(message, output);
			return true;
		}
		for (Map.Entry<String, ObjectOutputStream> kv : mapOnlines.entrySet()) {
			if (kv.getKey().equals(message.getName())) {
				message.setText("NÃO!");
				send(message, output);
				return false;
			} else {
				message.setText("SIM!");
				send(message, output);
				return true;
			}
		}
		return false;
	}

	private boolean createMatche(Message message, ObjectOutputStream output) {

		if (mapMatches.size() == 0) {
			message.setAction(Action.CREATE_GAME);
			message.setText("SIM!");
			send(message, output);
			return true;
		}
		for (Map.Entry<String, ObjectOutputStream> kv : mapMatches.entrySet()) {
			if (kv.getKey().equals(message.getPartida())) {
				message.setAction(Action.CREATE_GAME);
				message.setText("NÃO!");
				try {
					kv.getValue().writeObject(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return false;
			} else {
				message.setAction(Action.CREATE_GAME);
				message.setText("SIM!");
				try {
					kv.getValue().writeObject(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				;
				return true;
			}
		}
		return false;
	}

	private void remove(Message message) {
		mapMatches.remove(message.getPartida());
		sendMatches();
	}

	private void disconnect(Message message, ObjectOutputStream output) {
		mapOnlines.remove(message.getName());
		message.setText("Deixou a sala!");
		message.setAction(Action.SEND_ONE);
		sendAll(message);
		System.out.println("Usuario " + message.getName() + " saiu da sala");
	}

	private void sendOnlines() {
		Set<String> setNames = new HashSet<String>();
		for (Map.Entry<String, ObjectOutputStream> kv : mapOnlines.entrySet()) {
			setNames.add(kv.getKey());
		}
		Message message = new Message();
		message.setAction(Action.USERS_ONLINE);
		message.setSetOnlines(setNames);

		for (Map.Entry<String, ObjectOutputStream> kv : mapOnlines.entrySet()) {
			message.setName(kv.getKey());
			try {
				kv.getValue().writeObject(message);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	private void sendMatchesToOne(Message message, ObjectOutputStream output) {
		Set<String> setMatches = new HashSet<String>();
		this.mapOnlines.put(message.getName(), output);
		for (Map.Entry<String, ObjectOutputStream> kv : mapMatches.entrySet()) {
			setMatches.add(kv.getKey());
		}
		message.setAction(Action.MATCHES);
		message.setSetMatches(setMatches);
		System.out.println("TAM mapOnlines na conexão: "+mapOnlines.size());
		ObjectOutputStream output2 = mapOnlines.get(message.getName());
		try {
			output2.writeObject(message);
			System.out.println("TAM mapOnlines na conexão: "+mapOnlines.size());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void sendMatches() {
		Set<String> setMatches = new HashSet<String>();
		for (Map.Entry<String, ObjectOutputStream> kv : mapMatches.entrySet()) {
			setMatches.add(kv.getKey());
		}
		Message message = new Message();
		message.setAction(Action.MATCHES);
		message.setSetMatches(setMatches);
		for (Map.Entry<String, ObjectOutputStream> kv : mapOnlines.entrySet()) {
			message.setName(kv.getKey());
			try {
				kv.getValue().writeObject(message);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private void sendCreatedGame(Message message, ObjectOutputStream output) {
		message.setAction(Action.CREATE_GAME);
		try {
			output.writeObject(message);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void sendAll(Message message) {
		for (Map.Entry<String, ObjectOutputStream> kv : mapOnlines.entrySet()) {
			if (!kv.getKey().equals(message.getName())) {
				message.setAction(Action.SEND_ONE);
				try {
					kv.getValue().writeObject(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}

	private void send(Message message, ObjectOutputStream output) {
		try {
			output.writeObject(message);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void sendOne(Message message) {
		for (Map.Entry<String, ObjectOutputStream> kv : mapOnlines.entrySet()) {
			if (kv.getKey().equals(message.getNameReserved()))
				try {
					System.out.println("TAM DA LISTA: "
							+ message.getSetMatches());
					System.out.println("PARTIDA:");
					System.out.println("Nome: " + message.getName()
							+ " - acao: " + message.getAction() + " - Texto: "
							+ message.getText() + " - Partida: "
							+ message.getPartida());
					kv.getValue().writeObject(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
}
